
#include "cpu/cpu.h"
#include "ipc/mutex.h"
#include "os.cfg.h"
#include "comm/cpu_instr.h"
#include "cpu/irq.h"
#include "core/syscall.h"

static segment_desc_t gdt_table_new[GDT_TABLE_SIZE];
static mutex_t mutex;
/**
 * 释放一个段描述符
 */
void gdt_free_sel(int sel)
{
    mutex_lock(&mutex);
    gdt_table_new[sel / sizeof(segment_desc_t)].attr = 0;
    mutex_unlock(&mutex);
}
/**
 * 设置段描述符
 */
void segment_desc_set(int selector, uint32_t base, uint32_t limit, uint16_t attr)
{
    segment_desc_t *desc = gdt_table_new + (selector >> 3);

    // check the limit and setting Granularity
    if (limit > 0xfffff)
    {
        attr |= SEG_G;
        limit /= 0x1000;
    }

    // 实际上段限制长只有20位当存储为32位，在执行(attr | (limit >> 16) & 0xf << 8)指令时，
    // 确保传入其中的attr属性中limit是个比特位须初始化为0
    desc->limit15_0 = limit & 0xffff;
    desc->base15_0 = base & 0xffff;
    desc->base23_16 = (base >> 16) & 0xff;
    desc->base31_24 = (base >> 24) & 0xff;
    desc->attr = attr | (((limit >> 16) & 0xf) << 8);
    // desc->attr = (attr | (limit >> 16) & 0xf << 8);
}
/**
 * 初始化GDT
 */
void init_gdt(void)
{
    // clear the gdt, it init the gdt table
    for (int i = 0; i < GDT_TABLE_SIZE; i++)
    {
        segment_desc_set(i << 3, 0, 0, 0);
    }

    // now configurate the gdt table for your system code and data
    segment_desc_set(KERNEL_SELECTOR_CS, 0, 0xffffffff,
                     SEG_P_PRESENT | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_CODE | SEG_TYPE_RW | SEG_D);

    segment_desc_set(KERNEL_SELECTOR_DS, 0, 0xffffffff,
                     SEG_P_PRESENT | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_DATA | SEG_TYPE_RW | SEG_D);

    gate_desc_set((gate_desc_t *)(gdt_table_new + (SELECTOR_SYSCAL >> 3)), KERNEL_SELECTOR_CS, (uint32_t)exception_handler_syscall,
                  GATE_P_PRESENT | GATE_DPL3 | GATE_TYPE_SYSCALL | SYSCALL_PARAM_COUNT);
    // 系统调用的选择子位 KERNEL_SELECTOR_CS代码段，级内核代码段，将调用函数编译进内核，待用即可

    // so chinese english :(
    // note!!!C language,gdt_table is array,so when it as a parameter for function, it is address
    // the address for lgdt funcion, lgdt instruction let gdtr point the this addtess, it is a base
    // address,so now the gdt_table in where, the gdtr point where
    lgdt((uint32_t)gdt_table_new, sizeof(gdt_table_new));
}
/**
 * CPU初始化
 */
void cpu_init(void)
{
    //   init gdt table, it set the all item is zero
    init_gdt();
    mutex_init(&mutex);
}

/**
 * 分配一个GDT推荐表符
 */
int gdt_alloc_desc(void)
{
    int i;
        // 跳过第0项
        // irq_state_t state = irq_enter_protection();
        mutex_lock(&mutex);
    for ( i= 1; i < GDT_TABLE_SIZE; i++)
    {
        //     if (gdt_table_new[i].attr==0)
        //     {
        //         SEG_P_PRESENT;
        //         // irq_leave_protection(state);
        //         mutex_unlock(&mutex);
        //         return (i * sizeof(segment_desc_t));
        //    }
        segment_desc_t *desc = gdt_table_new + i;
        if (desc->attr == 0)
        {
            desc->attr = SEG_P_PRESENT; // 标记为占用状态
            break;
        }
    }
    // irq_leave_protection(state);
    mutex_unlock(&mutex);
    // return -1;
    return i >= GDT_TABLE_SIZE ? -1 : i * sizeof(segment_desc_t);
}
